모든 매개변수에 대하여 암시적 변환을 허용하기 위해서는 비멤버 함수를 사용해야 한다.
교환법칙이 성립하는 operator* 정의(mixed-mode)
template <typename T>
class Rational{
public:
Rational(const T& numerator=0, const T& denominator=1);
const T numerator() const;
const T denominator() const;
};
template <typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs){
}
이전 operator*에서 template로 선언된 점을 제외하면 다른것이 없다.
Rational<int> oneHalf(1, 2);
Rational<int> result=oneHalf*2;
하지만, 이전 코드와 달리 컴파일러는 2(int)로 부터, Rational<T>를 유추해 내지 못한다.
템플릿 인자 추론(template argument deduction)과정에서 암시적 타입 변환이 고려되지 않는다.
클래스 템플릿 내에서 비멤버 함수를 friend로 선언해 줌으로서 이를 해결해 줄 수 있다.
template <typename T>
class Rational{
public:
friend const Rational operator*(const Rational& lhs, const Rational& rhs);
};
template <typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs){
}
위와 같이 friend로 선언해 주게되면,
Rational<int>가 선언되면, Rational<int> 클래스 인스턴스가 생성되고,
Rational<int> 타입의 매개변수를 받는 프렌드 함수 operator*도 자동으로 선언된다.
하지만, 내부에서 선언된 함수는 Rational<int>에 대한 함수일 뿐, 아래의 템플릿으로 정의된 operator*가 아님
(링크 과정에서 애러가 발생할 것임)
class Rational{
public:
friend const Rational operator*(const Rational& lhs, const Rational& rhs){
return Rational(lhs.numerator()*rhs.numerator(), lhs.denominator()*rhs.denominator());
}
};
클래스 내에서 비멤버 함수를 선언하는 유일한 방법은 ‘프렌드’이다.
template <typename T> class Rational;
template <typename T> const Rational<T> doMultiPly(const Rational<T>& lhs, const Rational<T>& rhs);
template <typename T>
class Rational{
public:
friend const Rational<T> operator*(const Rational<T>& lhs, const Rational& rhs){
return doMultiply(lhs, rhs);
}
};
템플릿 클래스는 export 키워드를 사용해서 선언, 정의부를 분리할 수 있지만,
대다수의 컴파일러에서는 여전히 템플릿 정의를 헤더 파일에 포함하기를 요구함
template <typename T>
const Rational<T> doMultiply(const Rational<T>& lhs, const Rational<T>& rhs){
return Rational<T>(lhs.numerator()*rhs.numerator(), lhs.denominator()*rhs.denominator());
}
모든 매개변수에 대해 암시적 타입 변환을 지원하는 템플릿과 관계가 있는 함수를 제공하는 클래스 템플릿을 만들 때,클래스 템플릿 내에 프렌드 함수로서 정의하면 된다.